#include <linux/sem.h>
#include <asm/segment.h>
#include <unistd.h>
#include <asm/system.h>
#include <linux/kernel.h>
#include <string.h>

static sem_t _sem_head = {"\0", 0, NULL, NULL};
static sem_t *sem_head = &_sem_head;
//创建一个信号量，或打开一个已经存在的信号量
sem_t *sys_sem_open(const char *name, unsigned int value)
{
    char tmp[SEM_NAME_LEN] = {'\0'}; //暂存信号名
    int i = 0;
    if (name == NULL)
    {
        printk("NULL NAME FOR SEM\n");
        return NULL;
    }
    //复制结束，或者超过长度（截断）时退出循环
    // i最大值为SEM_NAME_LEN - 2,因为tmp[SEM_NAME_LEN - 1]必须为'\0'
    while ((tmp[i] = get_fs_byte(name + i)) != '\0' && i < SEM_NAME_LEN - 1)
        i++;
    if (i == 0)
    {
        printk("NAME LENGTH IS ZERO\n");
        return NULL;
    }
    //得到输入的参数字符串
    sem_t *p = sem_head->next;
    while (p != NULL)
    {
        if (!strcmp(tmp, p->name))
            return p; //返回已存在的信号量，无视参数value
        p = p->next;
    }
    // if not exist
    sem_t *q = malloc(sizeof(sem_t));
    strcpy(q->name, tmp);
    q->value = value;
    q->wait = NULL;
    // insert to link
    q->next = sem_head->next;
    sem_head->next = q;
    return q;
}

//信号量的 P 原子操作
int sys_sem_wait(sem_t *sem)
{
    if (!sem)
        return -1;
    cli();
    while (sem->value == 0)
        sleep_on(&sem->wait);
    sem->value--;
    sti();
    return 0;
}

//信号量的 V 原子操作
int sys_sem_post(sem_t *sem)
{
    if (!sem)
        return -1;
    cli();
    sem->value++;
    wake_up(&sem->wait);
    sti();
    return 0;
}

//删除名为 name 的信号量
int sys_sem_unlink(const char *name)
{
    char tmp[SEM_NAME_LEN] = {'\0'}; //暂存信号名
    int i = 0;
    if (name == NULL)
    {
        printk("NULL NAME FOR SEM\n");
        return NULL;
    }
    //复制结束，或者超过长度（截断）时退出循环
    // i最大值为SEM_NAME_LEN - 2,因为tmp[SEM_NAME_LEN - 1]必须为'\0'
    while ((tmp[i] = get_fs_byte(name + i)) != '\0' && i < SEM_NAME_LEN - 1)
        i++;
    if (i == 0)
    {
        printk("NAME LENGTH IS ZERO\n");
        return NULL;
    }
    //得到输入的参数字符串
    sem_t *q = sem_head, *p = q->next;
    while (p != NULL)
    {
        if (!strcmp(tmp, p->name))
            break;
        q = p;
        p = p->next;
    }
    if (p == NULL)
    {
        printk("not exist\n");
        return -1;
    }
    q->next = p->next;
    free(p);
    return 0;
}